Skip to content

feat!: replace dependency graph with reactive invalidation#5292

Open
DanySK wants to merge 30 commits intomasterfrom
feat/reactive-engine
Open

feat!: replace dependency graph with reactive invalidation#5292
DanySK wants to merge 30 commits intomasterfrom
feat/reactive-engine

Conversation

@DanySK
Copy link
Copy Markdown
Member

@DanySK DanySK commented Apr 21, 2026

Summary

This PR supersedes #5131.

It rebases the reactive-engine work on top of the current master and preserves the same overall direction: move the engine and the observable state model away from coarse-grained dependency recomputation and toward reactive, fine-grained invalidation.

At a high level, this branch:

  • introduces observable collections and lifecycle-aware observation primitives in alchemist-api
  • migrates engine-facing state changes to reactive observation
  • removes the old DependencyGraph / JGraphTDependencyGraph path
  • folds the reactive execution path back into Engine
  • tightens dependency tracking in SAPERE and related incarnations
  • updates affected tests and supporting code across engine, implementation base, loading, biochemistry, protelis, scafi, maps, physics, swing UI, and cognitive-agents

Why this exists

The original goal from #5131 remains the same:

  • reduce the amount of global recomputation triggered by local state changes
  • make engine scheduling and condition invalidation observable-driven
  • remove the explicit dependency-graph maintenance burden
  • improve runtime characteristics for workloads with frequent localized updates, especially SAPERE-like scenarios where the previous dependency invalidation strategy was too broad

Main changes

1. Observable model infrastructure

This PR adds a substantial observation layer in alchemist-api, including:

  • Observable
  • ObservableList
  • ObservableMap
  • ObservableSet
  • derived/event observables
  • lifecycle primitives (Lifecycle, LifecycleOwner, LifecycleRegistry, LifecycleState)

These components are then used to expose mutable simulation state in a way that can notify dependent logic incrementally instead of forcing coarse global refreshes.

2. Engine architecture

The branch first introduces a separate reactive engine during development, but the final rebased result does not keep a standalone ReactiveEngine class.

Instead, the final state:

  • removes DependencyGraph
  • removes JGraphTDependencyGraph
  • folds the reactive execution model into Engine
  • keeps AbstractEngine and related support code
  • updates tests accordingly

This is important because the PR is no longer “add a second engine implementation”; it is effectively “replace the old engine dependency-management model with a reactive one.”

3. Fine-grained invalidation in core model code

A large portion of the diff updates:

  • reactions
  • conditions
  • nodes
  • environments
  • linking rules

to subscribe to narrower observable sources and react only to relevant changes.

This includes changes in alchemist-implementationbase, where several conditions and reaction-related classes are updated to consume the new observable semantics.

4. SAPERE-focused performance work

The most performance-oriented part of the PR is the SAPERE work:

  • LsaNode
  • LsaNeighborhoodCondition
  • LsaStandardCondition
  • SAPEREGradient
  • SAPEREReaction

These changes aim to stop reacting to whole-LSA-space churn when only a targeted molecule or neighborhood-relevant subset changed.

This is likely the most immediately measurable performance improvement in the branch, because SAPERE workloads often stress dependency propagation and neighborhood-sensitive invalidation patterns.

5. Incarnation and integration updates

To keep the engine/model shift coherent, the branch also updates:

  • biochemistry neighbor conditions and related tests
  • protelis execution/context wiring
  • scafi action/condition paths
  • loading and extractor logic
  • maps/physics/cognitive-agents integration points

This is not incidental churn; most of these changes are required to make the reactive model consistent across the simulation stack.

What changed compared to #5131

This PR should be treated as the replacement for #5131, not as an unrelated follow-up.

Compared to #5131:

  • it is rebased on current master
  • conflict resolution keeps current master dependency versions where appropriate
  • the branch tip includes later cleanup/performance commits, including the final SAPERE fine-grained dependency work
  • the final engine shape is clearer: the old dependency-graph path is removed and the reactive path is integrated into Engine

Validation status

This branch was rebased with repository hooks enabled.

During the rebase, the only manual conflict resolutions required were:

  • alchemist-loading/src/main/kotlin/it/unibo/alchemist/boundary/loader/LoadingSystem.kt
  • gradle/libs.versions.toml

The resolutions were mechanical and aligned with the final intent of the rebased commits:

  • keep the integrated Engine(environment) path after the dedicated ReactiveEngine removal
  • keep master's newer shared dependency versions while preserving the branch’s added kotlinx-collections catalog entries

How to assess the performance change

There does not appear to be a dedicated microbenchmark suite in the repository, so the best assessment is comparative measurement on representative workloads.

Best candidates to measure

The most relevant areas are:

  • SAPERE simulations and tests
  • workloads with many localized state changes
  • scenarios with heavy neighborhood-sensitive conditions
  • cognitive-agent scenarios that repeatedly react to position/state updates

Practical ways to evaluate

  1. Compare master vs this branch using repeated runs of the same workloads.

  2. Focus first on SAPERE-heavy tests and simulations, because the final commits specifically optimize fine-grained SAPERE invalidation.

  3. Use wall-clock time, CPU time, allocation rate, and GC pressure as the primary metrics.

  4. Run on the same JVM and machine, ideally multiple times, ignoring the first run if dependency/classloading noise matters.

Concrete approaches

  • Run targeted Gradle test tasks repeatedly on master and this branch:

    • ./gradlew --parallel :alchemist-incarnation-sapere:test
    • ./gradlew --parallel :alchemist-engine:test
    • ./gradlew --parallel :alchemist-cognitive-agents:test
  • For scenario-level comparisons, build the simulator and execute the same representative simulations on both revisions, then compare:

    • end-to-end runtime
    • steady-state CPU usage
    • allocation / GC activity
  • If deeper profiling is needed, use JFR or an external profiler on identical simulations to confirm that:

    • fewer broad invalidation cascades occur
    • less time is spent in dependency recomputation / scheduling-related plumbing
    • SAPERE updates no longer trigger unnecessary wide recomputation

What I would expect to improve

If the branch behaves as intended, the most likely gains are:

  • better performance on SAPERE workloads with many local LSA updates
  • less unnecessary recomputation in condition/reaction invalidation
  • lower overhead from maintaining and traversing an explicit dependency graph

I would not assume uniform speedups everywhere. Some workloads may be neutral, and broad integration changes like this deserve empirical confirmation rather than a blanket performance claim.

Reference

Copilot AI review requested due to automatic review settings April 21, 2026 11:01
@DanySK DanySK self-assigned this Apr 21, 2026
@DanySK DanySK force-pushed the feat/reactive-engine branch from 5e27ac7 to 5cfd74d Compare April 21, 2026 11:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the engine’s coarse dependency-graph recomputation with a reactive invalidation model by introducing observable state primitives in alchemist-api and migrating engine/model code to reschedule based on fine-grained observation.

Changes:

  • Introduces observables (including observable collections) and lifecycle-aware observation primitives, and updates core APIs (Environment, Node, Condition, Actionable) accordingly.
  • Integrates reactive dependency invalidation into the engine execution path and removes the old dependency-graph approach.
  • Migrates multiple incarnations, utilities, UI components, and tests to the new observable APIs (e.g., getCurrentPosition, nodeCount.current, getNeighborhood(...).current).

Reviewed changes

Copilot reviewed 172 out of 174 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
gradle/libs.versions.toml Adds kotlinx immutable collections dependency version.
build.gradle.kts Propagates alchemist.engine system property to tests.
alchemist-web-renderer/src/jvmTest/kotlin/it/unibo/alchemist/boundary/server/utility/ToNodeSurrogateTest.kt Updates to getCurrentPosition API.
alchemist-web-renderer/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webui/server/surrogates/utility/ToNodeSurrogate.kt Uses getCurrentPosition for node surrogate.
alchemist-test/src/main/kotlin/it/unibo/alchemist/test/GlobalTestReaction.kt Implements reactive canExecute observation + lifecycle/dispose.
alchemist-test/src/main/kotlin/it/unibo/alchemist/test/Assertions.kt Uses getCurrentPosition in environment equality assertions.
alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/swingui/monitor/impl/NodeTracker.kt Updates displayed position to getCurrentPosition.
alchemist-swingui/src/main/kotlin/it/unibo/alchemist/boundary/swingui/effect/impl/DrawDirectedNode.kt Updates position + nodeCount.current usage in rendering.
alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/wormhole/impl/MapWormhole.java Uses getCurrentPosition for zoom fit.
alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/swingui/monitor/impl/Generic2DDisplay.java Uses getCurrentPosition and neighborhood .getCurrent().
alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/swingui/effect/impl/DrawSmartcam.java Uses getCurrentPosition for view calculations.
alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/swingui/effect/impl/DrawPedestrianPath.java Uses getCurrentPosition for path tracking.
alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/swingui/effect/impl/AbstractDrawOnce.java Uses getCurrentPosition for marker visibility.
alchemist-swingui/src/main/java/it/unibo/alchemist/boundary/swingui/effect/api/Effect.java Uses getCurrentPosition in default apply method.
alchemist-smartcam/src/main/kotlin/it/unibo/alchemist/model/actions/CameraSee.kt Uses getCurrentPosition when building visible nodes.
alchemist-smartcam/src/main/kotlin/it/unibo/alchemist/model/actions/CameraInjectVisibleNodeClosestToDistance.kt Uses getCurrentPosition for heading-based placement.
alchemist-physics/src/test/kotlin/it/unibo/alchemist/model/physics/environments/TestEuclideanPhysics2DEnvironment.kt Updates tests to getCurrentPosition.
alchemist-physics/src/main/kotlin/it/unibo/alchemist/model/physics/reactions/PhysicsUpdate.kt Makes physics update reaction reactive + lifecycle/dispose.
alchemist-physics/src/main/kotlin/it/unibo/alchemist/model/physics/environments/ContinuousPhysics2DEnvironment.kt Uses internal position retrieval consistently.
alchemist-physics/src/main/kotlin/it/unibo/alchemist/model/physics/environments/AbstractLimitedContinuous2D.kt Uses getCurrentPosition for movement constraints.
alchemist-physics/src/main/kotlin/it/unibo/alchemist/model/physics/actions/HeadTowardTarget.kt Uses getCurrentPosition for heading computation.
alchemist-physics/src/main/kotlin/it/unibo/alchemist/model/physics/InfluenceSphere2D.kt Uses getCurrentPosition for influence shape origin.
alchemist-physics/src/main/java/it/unibo/alchemist/model/physics/environments/MuseumHall.java Uses retrievePosition for movement/printing.
alchemist-maps/src/test/kotlin/it/unibo/alchemist/model/maps/linkingrules/TestInSightConnection.kt Uses nodeCount.current and neighborhood .current.
alchemist-maps/src/test/kotlin/it/unibo/alchemist/model/maps/actions/TestTargetMapWalker.kt Updates position assertions to getCurrentPosition.
alchemist-maps/src/main/kotlin/it/unibo/alchemist/model/maps/actions/RandomTargetInPolygonOnMap.kt Uses getCurrentPosition for collision target updates.
alchemist-maps/src/main/java/it/unibo/alchemist/model/maps/movestrategies/speed/StraightLineTraceDependantSpeed.java Uses getCurrentPosition for distance computation.
alchemist-maps/src/main/java/it/unibo/alchemist/model/maps/linkingrules/LinkNodesWithinRoutingRange.java Adjusts generics for node stream typing.
alchemist-maps/src/main/java/it/unibo/alchemist/model/maps/environments/OSMEnvironment.java Uses retrievePosition when computing routes.
alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestSpecificPositions.kt Uses getCurrentPosition in YAML loading test.
alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestGraphStreamReproducibility.kt Uses getCurrentPosition and neighborhood .current.
alchemist-loading/src/test/kotlin/it/unibo/alchemist/test/TestGraphStream.kt Uses nodeCount.current and neighborhood .current.
alchemist-loading/src/test/java/it/unibo/alchemist/model/TestYAMLLoader.java Uses getCurrentPosition in loader tests.
alchemist-loading/src/test/java/it/unibo/alchemist/model/TestLoadGPSTrace.java Uses nodeCount.getCurrent() and getCurrentPosition.
alchemist-loading/src/main/kotlin/it/unibo/alchemist/model/util/GraphStreamSupport.kt Uses nodeCount.current when building linking rule.
alchemist-loading/src/main/kotlin/it/unibo/alchemist/model/deployments/CloseToAlreadyDeployed.kt Uses getCurrentPosition when extracting sources.
alchemist-loading/src/main/kotlin/it/unibo/alchemist/boundary/extractors/NumberOfNodes.kt Exports nodeCount.current.
alchemist-loading/src/main/kotlin/it/unibo/alchemist/boundary/extractors/NodesPositions.kt Uses nodeCount.current + getCurrentPosition.
alchemist-loading/src/main/kotlin/it/unibo/alchemist/boundary/extractors/NodeDegree.kt Uses neighborhood .current for degree.
alchemist-loading/src/main/kotlin/it/unibo/alchemist/boundary/extractors/NetworkDensity.kt Uses getCurrentPosition and nodeCount.current.
alchemist-loading/src/main/kotlin/it/unibo/alchemist/boundary/extractors/NetworkCentroid.kt Uses nodeCount.current + getCurrentPosition.
alchemist-loading/build.gradle.kts Removes Arrow dependency from loading module.
alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/scafi/conditions/ScafiComputationalRoundComplete.scala Adds observable dependency to condition.
alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/scafi/actions/SendScafiMessage.scala Uses neighborhood .getCurrent.
alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/scafi/actions/RunScafiProgram.scala Adds observable computational-cycle completion signal.
alchemist-incarnation-scafi/src/main/scala/it/unibo/alchemist/model/scafi/ScafiIncarnationForAlchemist.scala Uses getCurrentPosition for layer reads.
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/reactions/SAPEREReaction.java Uses getCurrentPosition and neighborhood .getCurrent().
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/reactions/SAPEREGradient.java Adds disposables + switches to reactive dependencies.
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/conditions/LsaStandardCondition.java Reworks validity/propensity using observables.
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/conditions/LsaNeighborhoodCondition.java Adds fine-grained neighbor molecule-name observation.
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/actions/SAPEREWalkerRiseGradient.java Uses getCurrentPosition and neighborhood .getCurrent().
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/actions/LsaRandomNeighborAction.java Uses neighborhood .getCurrent().
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/actions/LsaCountNeighborsAction.java Uses neighborhood .getCurrent().
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/actions/LsaAscendingGradientDist.java Caches neighborhood via onChange subscription.
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/actions/AbstractSAPERENeighborAgent.java Uses getCurrentPosition + neighborhood .getCurrent().
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/actions/AbstractSAPEREMoveNodeAgent.java Uses getCurrentPosition + neighborhood .getCurrent().
alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/ILsaNode.java Adds observable LSA-space and molecule-name observation APIs.
alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/properties/ProtelisDevice.kt Uses getCurrentPosition for layer fallback reads.
alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/actions/RunProtelisProgram.kt Exposes computational-cycle completion as observable.
alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/AlchemistNetworkManager.kt Uses neighborhood .current and caches neighbor devices set.
alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/AlchemistExecutionContext.kt Uses getCurrentPosition in context API.
alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/incarnations/ProtelisIncarnation.kt Updates Node stub to include new observable/lifecycle APIs.
alchemist-incarnation-protelis/src/main/java/it/unibo/alchemist/model/protelis/conditions/ComputationalRoundComplete.java Hooks condition validity into program observable completion.
alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/model/biochemistry/reactions/TestNeighborhoodReactionsPropensities.kt Migrates junction map access to observable maps.
alchemist-incarnation-biochemistry/src/test/kotlin/it/unibo/alchemist/model/biochemistry/molecules/TestMoleculeSwapWithinNeighborhood.kt Uses neighborhood .current in assertions.
alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/properties/TestDeformableCell.java Uses getCurrentPosition in tests.
alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/nodes/TestEnvironmentNodes.java Uses getCurrentPosition for environment queries.
alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/layers/TestBiomolLayer.java Uses getCurrentPosition in layer test.
alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/actions/TestChemotaxis.java Uses nodeCount.getCurrent() + getCurrentPosition.
alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/biochemistry/properties/CircularDeformableCell.kt Switches junctions storage to observable maps.
alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/biochemistry/properties/CircularCell.kt Switches junctions storage to observable maps.
alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/biochemistry/properties/Cell.kt Switches junctions storage to observable maps.
alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/biochemistry/conditions/BiomolPresentInNeighbor.kt Reimplements condition using reactive neighbor propensities.
alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/biochemistry/actions/ChangeBiomolConcentrationInNeighbor.kt Uses neighborhood .current and guards empty neighbor set.
alchemist-incarnation-biochemistry/src/main/kotlin/it/unibo/alchemist/model/biochemistry/CellProperty.kt Refactors junction operations for observable maps.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/environments/BioRect2DEnvironmentNoOverlap.java Uses retrievePosition in overlap checks and movement.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/environments/BioRect2DEnvironment.java Uses retrieved neighborhood + observable junctions map.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/TensionPresent.java Makes validity/propensity reactive via nodes-in-range observation.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/NeighborhoodPresent.java Makes neighbor presence validity reactive.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/JunctionPresentInCell.java Makes junction presence + propensity reactive.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/GenericMoleculeUnderLevel.java Derives validity/propensity from observable concentration.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/GenericMoleculePresent.java Derives validity/propensity from observable concentration.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/EnvPresent.java Reacts to neighborhood changes for environment-node presence.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/BiomolPresentInNeighbor.java Removes legacy non-reactive implementation.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/BiomolPresentInEnv.java Makes total-quantity validity/propensity observable-driven.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/conditions/AbstractNeighborCondition.java Refactors neighbor validity/propensity to reactive model.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/actions/ChemotacticPolarization.java Caches neighborhood-derived list via onChange; uses getCurrentPosition.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/actions/ChangeBiomolConcentrationInEnv.java Uses neighborhood .getCurrent() for env nodes.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/actions/CellTensionPolarization.java Uses getCurrentPosition for vector computations.
alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/actions/AbstractNeighborAction.java Uses neighborhood .getCurrent() in default execute.
alchemist-implementationbase/src/test/kotlin/it/unibo/alchemist/util/TestEnvironmentsDiameter.kt Removes unused imports; updates copyright year.
alchemist-implementationbase/src/test/kotlin/it/unibo/alchemist/model/timedistributions/TestSimpleNetworkArrivals.kt Uses neighborhood .current in mocks.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/util/Environments.kt Uses nodeCount.current + neighborhood .current for metrics.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/SimpleNetworkArrivals.kt Uses neighborhood .current for adjacency.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/terminators/StableForSteps.kt Uses getCurrentPosition + nodeCount.current.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/nodes/GenericNode.kt Adds observable contents + lifecycle/dispose semantics.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/movestrategies/target/FollowTarget.kt Uses getCurrentPosition.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/linkingrules/FullyConnected.kt Uses nodeCount.current.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/linkingrules/ConnectViaAccessPoint.kt Uses neighborhood .current in link decision.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/conditions/NoOtherReactionCanExecute.kt Makes validity reactive via observeCanExecute().
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/conditions/NeighborHasConcentration.kt Adds new reactive neighborhood concentration condition.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/conditions/ContainsMolecule.kt Makes validity reactive using observeContains.
alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/conditions/AbstractNonPropensityContributingCondition.kt Propensity derived from observeValidity().
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/reactions/ChemicalReaction.java Adjusts initialization hook method name/signature.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/linkingrules/ObstaclesBreakConnection.java Uses getCurrentPosition during obstacle checks.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/linkingrules/ClosestN.java Uses nodeCount.getCurrent() and adjusts generics.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/conditions/MoleculeHasConcentration.java Makes validity/propensity reactive via observable concentration.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/conditions/ConcentrationChanged.java Makes change-detection reactive and updates propensity.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/conditions/AbstractCondition.java Introduces reactive validity/propensity + inbound observable deps.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/actions/MoveForwardAndTeleport.java Uses getCurrentPosition for movement base.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/actions/AbstractMoveNode.java Uses getCurrentPosition for movement computation.
alchemist-implementationbase/src/main/java/it/unibo/alchemist/model/actions/AbstractConfigurableMoveNode.java Uses getCurrentPosition for target planning.
alchemist-graphql/src/jvmTest/kotlin/it/unibo/alchemist/boundary/graphql/schema/model/EnvironmentSurrogateTest.kt Updates to getCurrentPosition and neighborhood .current.
alchemist-graphql-surrogates/src/main/kotlin/it/unibo/alchemist/boundary/graphql/schema/model/surrogates/EnvironmentSurrogate.kt Uses getCurrentPosition and neighborhood .current.
alchemist-euclidean-geometry/src/test/java/it/unibo/alchemist/model/obstacles/TestContinuous2DObstacle.java Uses nodeCount.getCurrent() in assertions.
alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/movestrategies/RandomTarget.kt Uses getCurrentPosition supplier.
alchemist-euclidean-geometry/src/main/kotlin/it/unibo/alchemist/model/actions/FollowAtDistance.kt Uses getCurrentPosition in movement.
alchemist-euclidean-geometry/src/main/java/it/unibo/alchemist/model/linkingrules/ConnectionBeam.java Uses getCurrentPosition for obstacle-aware linking.
alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/util/DependencyUtils.kt Adds test utility for dependency assertions.
alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/TestReactiveDependencies.kt New test asserting reactive reschedule requests.
alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/TestDependencyGraph.kt Removes legacy dependency-graph test.
alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/EngineTest.kt Adds engine integration test for reactive neighbor condition.
alchemist-engine/src/test/kotlin/it/unibo/alchemist/core/AbstractDependencyTest.kt Refactors dependency test base for new assertions.
alchemist-engine/src/main/kotlin/it/unibo/alchemist/core/batch/BatchManager.kt Adds batching helper for reschedule requests.
alchemist-engine/src/main/kotlin/it/unibo/alchemist/core/BatchEngine.kt Removes dependency-graph updates; integrates reactive rescheduling path.
alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/model/cognitive/properties/TestPhysicalPedestrians.kt Uses getCurrentPosition in behavior assertions.
alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/model/cognitive/environments/TestEnvironmentWithDynamics.kt Uses nodeCount.current in checks.
alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/model/cognitive/TestSteeringBehaviors.kt Uses getCurrentPosition for distance checks.
alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/model/cognitive/TestOrientingBehavior.kt Uses getCurrentPosition in assertions and path checks.
alchemist-cognitive-agents/src/test/kotlin/it/unibo/alchemist/model/cognitive/TestFeelsTransmission.kt Uses getCurrentPosition in movement assertions.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/steering/Weighted.kt Uses getCurrentPosition for closest-target selection.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/properties/PhysicalPedestrian2D.kt Uses getCurrentPosition for shapes/repulsion.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/properties/Cognitive.kt Uses getCurrentPosition for layer-based danger.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/navigation/ReachKnownDestination.kt Uses getCurrentPosition for graph routing decisions.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/navigation/Explore.kt Uses getCurrentPosition when sampling node density.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/environments/EnvironmentWithDynamics.kt Overrides getCurrentPosition and syncs body position.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/actions/CognitiveAgentAvoidLayer.kt Uses getCurrentPosition in danger detection.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/actions/AbstractNavigationAction.kt Uses getCurrentPosition for navigation state.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/actions/AbstractGroupSteeringAction.kt Uses getCurrentPosition for centroid computation.
alchemist-cognitive-agents/src/main/kotlin/it/unibo/alchemist/model/cognitive/SteeringActionWithTarget.kt Uses getCurrentPosition for target distance.
alchemist-api/src/test/kotlin/it/unibo/alchemist/model/observation/ObservableMapTest.kt Adds tests for observable map semantics.
alchemist-api/src/test/kotlin/it/unibo/alchemist/model/observation/LifecycleTest.kt Adds tests for lifecycle-bound observation.
alchemist-api/src/test/kotlin/it/unibo/alchemist/model/TestTerminationPredicateSerialization.kt Uses nodeCount.current in predicate serialization test.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/lifecycle/LifecycleState.kt Adds lifecycle state enum.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/lifecycle/LifecycleRegistry.kt Adds lifecycle registry implementation.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/lifecycle/LifecycleOwner.kt Adds lifecycle owner + bindTo helper.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/lifecycle/Lifecycle.kt Adds lifecycle interface.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/EventObservable.kt Adds non-idempotent event observable for signals.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/Disposable.kt Adds common disposable abstraction.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/observation/DerivedObservable.kt Adds base class for derived observables.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/Node.kt Adds observable contents + lifecycle/dispose to nodes.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/EuclideanEnvironment.kt Updates default move to getCurrentPosition.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/Environment.kt Makes neighborhood/position/node count reactive.
alchemist-api/src/main/kotlin/it/unibo/alchemist/model/Actionable.kt Adds observeCanExecute, lifecycle/dispose, reschedule observable.
alchemist-api/src/main/java/it/unibo/alchemist/model/Condition.java Adds reactive validity/propensity + observable deps + dispose.
alchemist-api/src/main/java/it/unibo/alchemist/core/DependencyGraph.java Removes old dependency-graph API.
alchemist-api/build.gradle.kts Promotes Arrow + kotlinx immutable collections to API deps.
.idea/inspectionProfiles/Project_Default.xml Disables IntelliJ DataFlowIssue inspection.
Files not reviewed (1)
  • .idea/inspectionProfiles/Project_Default.xml: Language not supported

Comment on lines +73 to +76
set(value) {
field = value
field.forEach(Disposable::dispose)

Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the conditions setter you assign field = value and then immediately dispose field, which disposes the new conditions instead of the previously installed ones. This likely breaks subsequent subscriptions (observeInboundDependencies) and makes conditions unusable. Dispose the old field before overwriting it, then wire observers on the new list.

Copilot uses AI. Check for mistakes.
Comment on lines 153 to +160
if (nextEvent.canExecute()) {
safeExecuteEvent(nextEvent)
safeUpdateEvent(nextEvent)
}

nextEvent.update(currentLocalTime, true, environment)
scheduler.updateReaction(nextEvent)
synchronized(scheduler) {
scheduler.updateReaction(nextEvent)
}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nextEvent.update(currentLocalTime, true, environment) is invoked unconditionally with hasBeenExecuted = true, even when nextEvent.canExecute() is false and the reaction was not executed. This violates the Actionable.update contract and can cause incorrect rescheduling/time-distribution updates. Pass hasBeenExecuted based on whether the reaction actually executed.

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +84
val inner = junctions[junction].currentOrNull()
inner?.let {
when (it[neighbor].current.getOrElse { 0 }) {
1 -> it.remove(neighbor)
else -> it[neighbor].currentOrNull()?.minus(1)
}
if (it.isEmpty()) {
junctions.remove(junction)
} else {
junctions[junction] = inner
}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In removeJunction, the else -> it[neighbor].currentOrNull()?.minus(1) branch computes a decremented value but never writes it back to the inner map, so counts > 1 will never decrease. Update the map entry (e.g., set the new value) when decrementing, and ensure the updated value is stored in the observable map.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +65
override fun cloneCondition(
newNode: Node<Double>,
newReaction: Reaction<Double>,
): AbstractNeighborCondition<Double> = BiomolPresentInNeighbor(environment, node, molecule, concentration)

Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cloneCondition ignores the newNode parameter and rebuilds the condition using the original node, which will attach the cloned condition to the wrong node (and potentially wrong environment context). Recreate the condition with newNode (and the appropriate environment reference) instead.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e27ac7221

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

return Disposable {
this@bindTo.stopWatching(dataListener)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Unregister lifecycle bindings with the correct registrant

The disposable returned by bindTo unregisters with stopWatching(dataListener), but the subscription was created with onChange(lifecycleOwner, ...). Because the registrant keys do not match, dispose() does not actually detach the observer, so repeated rebindings (for example when reactions are reinitialized) accumulate stale callbacks and can trigger duplicate updates/reschedule requests.

Useful? React with 👍 / 👎.

override fun stopWatching(registrant: Any) {
observers -= registrant
observingCallbacks.remove(registrant)
backing.stopWatching(registrant)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop union observers using the same registration key

union.onChange registers callbacks in backing with the composite key this to registrant, but union.stopWatching removes only registrant. This mismatch leaves observers subscribed in backing after callers unsubscribe, causing leaked subscriptions and continued callback delivery to supposedly removed watchers.

Useful? React with 👍 / 👎.

override fun toList(): List<T> = backing

override fun copy(): ObservableMutableList<T> = ObservableMutableList<T>().apply {
backing = this@ObservableMutableList.backing
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve observable size when copying observable lists

copy() duplicates only backing, so the new list’s internal sizeObservable remains at its default 0 even when the copied list is non-empty. Any code that reads copy().observableSize.current right after cloning gets an incorrect value until a later mutation happens to resync it.

Useful? React with 👍 / 👎.

newPosition == null -> { // removal
if (node in region.visibleNodes) region.visibleNodes.remove(node)
regionNodeCenteredIndex.remove(node.id)?.forEachValue {
regionObservers.remove(it)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid removing region observers during list iteration

This method iterates regionObservers and, in the node-removal path, also removes elements from the same list. When node-centered range observers exist, removing a node can trigger ConcurrentModificationException and break the environment update flow. Defer removals or use an iterator-based removal strategy.

Useful? React with 👍 / 👎.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.53%. Comparing base (59559e5) to head (79c0f7e).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##             master    #5292   +/-   ##
=========================================
  Coverage     61.53%   61.53%           
  Complexity       14       14           
=========================================
  Files             2        2           
  Lines            78       78           
  Branches          4        4           
=========================================
  Hits             48       48           
  Misses           24       24           
  Partials          6        6           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@DanySK DanySK force-pushed the feat/reactive-engine branch 2 times, most recently from 1da8723 to c36bbea Compare April 22, 2026 11:19
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 28, 2026

Hi @DanySK! 👋
This pull request has conflicts 😖
Could you fix it? 🔧
Thank you! 🙏

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 28, 2026

⚠️ The sha of the head commit of this PR conflicts with #5131. Mergify cannot evaluate rules on this PR. Once #5131 is merged or closed, Mergify will resume processing this PR. ⚠️

@DanySK DanySK force-pushed the feat/reactive-engine branch from 134af6e to efa9acf Compare April 28, 2026 17:03
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 28, 2026

⚠️ The sha of the head commit of this PR conflicts with #5131. Mergify cannot evaluate rules on this PR. Once #5131 is merged or closed, Mergify will resume processing this PR. ⚠️

@DanySK DanySK force-pushed the feat/reactive-engine branch 3 times, most recently from d1caefc to cd7d649 Compare April 28, 2026 23:05
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 29, 2026

Hi @DanySK! 👋
This pull request has conflicts 😖
Could you fix it? 🔧
Thank you! 🙏

@DanySK DanySK force-pushed the feat/reactive-engine branch 2 times, most recently from ea9856f to 2a7f507 Compare April 29, 2026 22:09
S-furi and others added 9 commits April 30, 2026 01:56
# Conflicts:
#	.github/workflows/build-and-deploy.yml
#	.github/workflows/update-ancillary-files.yml
#	CHANGELOG.md
#	dokka-cache/ch.qos.logback/logback-classic/1.5.21.list
#	dokka-cache/ch.qos.logback/logback-classic/1.5.22.list
#	dokka-cache/ch.qos.logback/logback-classic/1.5.26.list
#	dokka-cache/de.flapdoodle.embed/de.flapdoodle.embed.mongo/4.21.0.list
#	dokka-cache/de.flapdoodle.embed/de.flapdoodle.embed.mongo/4.22.0.list
#	dokka-cache/de.flapdoodle.embed/de.flapdoodle.embed.mongo/4.24.0.list
#	dokka-cache/it.unibo.alchemist/alchemist-api/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-api/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-api/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-cognitive-agents/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-cognitive-agents/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-cognitive-agents/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-composeui/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-composeui/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-composeui/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-engine/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-engine/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-engine/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-euclidean-geometry/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-euclidean-geometry/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-euclidean-geometry/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-full/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-full/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-full/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-graphql-surrogates/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-graphql-surrogates/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-graphql-surrogates/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-graphql/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-graphql/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-graphql/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-implementationbase/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-implementationbase/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-implementationbase/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-biochemistry/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-biochemistry/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-biochemistry/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-protelis/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-protelis/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-protelis/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-sapere/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-sapere/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-incarnation-sapere/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-loading/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-loading/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-loading/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-maintenance-tooling/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-maintenance-tooling/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-maintenance-tooling/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-maps/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-maps/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-maps/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-physics/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-physics/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-physics/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-sapere-mathexp/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-sapere-mathexp/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-sapere-mathexp/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-smartcam/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-smartcam/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-smartcam/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-swingui/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-swingui/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-swingui/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-test/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-test/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-test/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-ui-tooling/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-ui-tooling/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-ui-tooling/42.3.32.list
#	dokka-cache/it.unibo.alchemist/alchemist-web-renderer/42.3.18.list
#	dokka-cache/it.unibo.alchemist/alchemist-web-renderer/42.3.22.list
#	dokka-cache/it.unibo.alchemist/alchemist-web-renderer/42.3.32.list
#	gradle/libs.versions.toml
#	package-lock.json
#	package.json
#	settings.gradle.kts

# Conflicts:
#	alchemist-engine/src/main/kotlin/it/unibo/alchemist/core/Engine.kt

# Conflicts:
#	alchemist-api/build.gradle.kts
#	alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/environments/AbstractEnvironment.kt
#	alchemist-loading/src/main/kotlin/it/unibo/alchemist/boundary/loader/LoadingSystem.kt
S-furi and others added 21 commits April 30, 2026 01:56
…l callback

Now the "laziness" concept is applied to dervied observables too.
For actions in general, it is better to use an imperative approach and retrieve the current neighborhood instead of having an up-to-date neighborhood version stored locally.
…ency graph

fix: remove obsolete reactive engine loading
chore(engine): use explicit synchronisation instead of thread contexts
This idea is borrowed by Androidx, where by means of Lifecycle state machines we are able to bound the lifecycle of the dependencies to the lifecycle of the owner (i.e. the registrant), properly disposing and releasing observers references withtout using weak references, hence not impacting too much negatively performance.
commit cbc430612a9a9063264da4f229fec1f7dc270ecd
Author: S-furi <stefano.furi7@gmail.com>
Date:   Fri Feb 20 14:02:00 2026 +0100

    perf(api): make use of persistent collections in observable collections

    this led to a great improvement in memory usage especially for neighbor-related streams where set of nodes where occupying large chunks of memory.

    This usage of persistent structures improved equality checks speed with an overall time reduction of approx a 10% wrt standard kotlin collections usage (from 22s independent test (SAPERE) to 20s)

commit ef9301aed3555d7d97c262a755085ace39bc9c24
Author: S-furi <stefano.furi7@gmail.com>
Date:   Fri Feb 20 11:53:17 2026 +0100

    perf(engine): introduce `BatchManager` to avoid redundant reschedule requests

commit 6c04f40fcea4ce9f644cf2b9e162b999199d524e
Author: S-furi <stefano.furi7@gmail.com>
Date:   Fri Feb 20 11:18:14 2026 +0100

    perf(sapere): introduce finer-grained observables for lsa spaces
@DanySK DanySK force-pushed the feat/reactive-engine branch from 2a7f507 to 79c0f7e Compare April 29, 2026 23:56
@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants